package net.aramzamzam.tapestry.judo.services.internal;

import static java.lang.String.format;
import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import net.aramzamzam.tapestry.judo.services.ExtraModulesService;
import net.aramzamzam.tapestry.judo.services.WidgetService;

import org.apache.tapestry5.Asset;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.Request;



public class WidgetServiceImpl implements WidgetService
{
	
    private final Helper _builder;

    private final List<String> _coreScripts;

    private boolean _coreAssetsAdded;

	private final String dojoRoot;
	private final Set<String> loadedModules;

	private final String djConfig;

	private final boolean isXDomain;
	private final String dojoUrl;
	private static final String scriptUrlFormat="/%1$s%2$s%3$s.js";
	private static final String CREATE_WIDGET="judo.create('%1$s', '%2$s');";
	private static final String REQUIRE_MODULE="dojo.require('%1$s');";
	private final Map<String, String> modules;

	private final boolean isDebug;

	private final Request request;

	private final TypeCoercer coercer;

	private final AssetSource assetSource;

	private final SymbolSource symbolSource;

	private final Locale locale;

    public WidgetServiceImpl(TypeCoercer coercer, 
    								Helper builder, 
    								Request request, 
    								String dojoRoot, 
    								boolean isXDomain,
    								boolean uncompressed,
    								boolean isDebug,
    								Locale locale,
    								ExtraModulesService extraModules,
    								SymbolSource symbolSource,
    								AssetSource assetSource
    								)
    {
    	modules=Collections.synchronizedMap(new HashMap<String, String>());
    	this.coercer = coercer;
		_builder = builder;
		this.request = request;
		this.locale = locale;
		this.symbolSource = symbolSource;
		this.assetSource = assetSource;
	    		
		this.dojoRoot = resolveUrl(dojoRoot);
		this.isXDomain = isXDomain;
		this.isDebug = isDebug;
		
		dojoUrl=this.dojoRoot+String.format(scriptUrlFormat, "dojo/dojo", this.isXDomain?".xd":"", uncompressed?".js.uncompressed":"");
		String language=locale.getLanguage().toLowerCase();
		String country=locale.getCountry().toLowerCase();
		String dojoLocale = country.isEmpty()?language:language+"-"+country;
		Map<String, String> modules = extraModules.getModules();
		StringBuilder smodules=new StringBuilder();
		for (String mname : modules.keySet()) {
			String path=resolveUrl(modules.get(mname));
			this.modules.put(mname, path);
			smodules.append(",\"");
			smodules.append(mname);
			smodules.append("\":\"");
			smodules.append(path);
			smodules.append('"');
		}
		if (smodules.length()>0)
			smodules.delete(0, 1);
        djConfig=String.format("useXDomain:%1$b,isDebug:%2$b, locale:'%3$s', modulePaths: { %4$s }",isXDomain,isDebug,dojoLocale, smodules.toString());
		Vector<String> scripts=new Vector<String>();
		if (scripts.size()>0)
			_coreScripts=scripts.subList(0, scripts.size());
		else
			_coreScripts=new LinkedList<String>();
        loadedModules=new HashSet<String>();
    }

    private String resolveUrl(String s)
    {
    	String expanded = symbolSource.expandSymbols(s);
	    Asset asset = assetSource.getAsset(null, expanded, locale);
	    return asset.toClientURL();
    }
    
    @Override
    public void require(String module)
    {
         if (isDebug)
        	 requireModule(module);
         else
        	 addCore();
    }
    
    private void requireModule(String module)
    {
    	 addCore();
         if (!loadedModules.contains(module))
         {
         	loadedModules.add(module);
         	String script = format(REQUIRE_MODULE, module);
         	_builder.addScriptHead(script);
         }
    }
    
    @Override
    public void requireLayer(String layer)
    {
    	requireModule(layer);
    }
    
    @Override
    public void addOnLoad(String format, Object... arguments)
    {
        notNull(format, "format");

        addCore();

        String script = format(format, arguments);

        _builder.addScriptOnDomLoad(script);
    }
    
    private void addCore()
    {
        if (!(_coreAssetsAdded || request.isXHR()))
        {
        	_builder.addScriptLink(dojoUrl,"djConfig",djConfig);
        	for (String path : _coreScripts)
            	_builder.addScriptLink(path);

            _coreAssetsAdded = true;
        }
    }

	@Override
	public void requireCss(String module, String path) {
		String m=modules.get(module);
		String p=(m==null?(dojoRoot+"/"+module):m)+"/"+path;
		_builder.addStylesheetLink(p, null);
	}

	@Override
	public void createWidget(String dojoType, String dojoModule, Element element, ComponentResources resources, Object... widgetParameters) {
		// adds dojoType for dojoParser
		if (dojoModule!=null)
			require(dojoModule);
		else
			require(dojoType);
        createWidgetNoRequire(dojoType, element, resources, widgetParameters);
	}

    @Override
    public void createWidget(String dojoType, Element element, ComponentResources resources, Object... widgetParameters)
    {
        require(dojoType);
        createWidgetNoRequire(dojoType, element, resources, widgetParameters);
    }
    
    private void createWidgetNoRequire(String dojoType, Element element, ComponentResources resources, Object... widgetParameters){
        String id=element.getAttribute("id");
        if (id==null || id.isEmpty())
            throw new RuntimeException("element doesnot have id attribute");

        List<String> informals = resources.getInformalParameterNames();
        for (Object p : informals) {
            String pp=(String)p;
            String v=resources.getInformalParameter(pp, String.class);
            if ("class".equals(pp)) //zone puts class name parameter before all
            	element.addClassName(v);
            else
            	element.attribute(pp, v);
        }
        
        for (int i = 0; i < widgetParameters.length; i+=2) {
            String p = (String)widgetParameters[i];
            Object value = widgetParameters[i+1];
            if (value == null) continue;
            if (value instanceof Block) continue;
            String valueString = coercer.coerce(value, String.class);
            if ("class".equals(p)) //zone puts class name parameter before all
            	element.addClassName(valueString);
            else
            	element.attribute(p, valueString);
        }

        if (request.isXHR())
        {
            element.attribute("dojoType", dojoType);
        }
        else
        {
            _builder.addScriptBottom(String.format(CREATE_WIDGET, id, dojoType));
            require("judo.creator");
        }
    }
}